package com.rw.map.service;

import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.rw.map.config.APIConfig;
import com.rw.map.dao.NavigateDao;
import com.rw.map.model.CameraDataDTO;
import com.rw.map.model.HeatMapQueryParam;
import com.rw.map.model.HeatMapVO;
import com.rw.map.model.HistoryQueryParam;
import com.rw.map.model.LinesDTO;
import com.rw.map.model.NavigateVO;
import com.rw.map.model.Path;
import com.rw.map.model.PersonInfo;
import com.rw.map.model.PersonLines;
import com.rw.map.model.PersonlinesVO;
import com.rw.map.model.Point;
import com.rw.map.model.ResultDataVO;
import com.rw.map.model.ShopFlowQueryParam;
import com.rw.map.model.TrailDetailIncQueryDTO;
import com.rw.map.model.TrailDetailQueryDTO;
import com.rw.map.model.UserInfoDTO;
import com.rw.map.utils.DateUtils;
import com.rw.map.utils.NaviTool;
import com.rw.map.utils.RedisUtil;
import com.rw.map.utils.RestTemplateUtils;
import com.rw.map.utils.StringUtils;

/**
 * 
 * @author forest
 *
 */
@Service
public class NavigateService {

	@Autowired
	private APIConfig apiConfig;

	@Autowired
	private NavigateDao navigateDao;
	
	@Autowired
	private RedisUtil redisUtil;
	
	@Autowired
	private NavigateFeignClient feignClient;
	
	@Autowired
	private MerchantFeignClient mFeignClient;
	
	@Autowired
	private RestTemplateUtils restTemplateUtils;

	private static Logger logger = LoggerFactory.getLogger(NavigateService.class);
	
	/**
	 * 热力图
	 * @param p
	 * @return
	 */
	public List<HeatMapVO> heatMapCount(HeatMapQueryParam p){
		p.setMarkNodes(getNaviPathCamera(p.getNodeCode()));
		//String result = feignClient.heatMap(p);
		String result = restTemplateUtils.httpRequestByJSON(JSON.toJSONString(p), "http://58.251.16.82:7000/heatMap/count");
		List<HeatMapVO> list = new ArrayList<>();
		if(StringUtils.isBlank(result)) {return list;}
		JSONObject obj = JSON.parseObject(result);
		if(obj.getInteger("code") != 0) {
			logger.error("调用heatMap()异常=====>{}",obj.getString("message"));
			return list;
		}
		// 初始化NaviTool
		NaviTool naviTool = this.initNaviTool(obj.getJSONObject("data").getString("nodeCode"), p.getBuildingId()); 
		JSONArray nodes = obj.getJSONObject("data").getJSONArray("nodes");
		for(int i = 0; i < nodes.size(); i++) {
			List<Point> pointList = new ArrayList<>();
			JSONObject o = nodes.getJSONObject(i);
			// 通过NaviTool提供的算法得出Points
			try {
				pointList = naviTool.navigateByCamera(o.getString("nodeCode"), o.getString("relateNodeCode"));
				for(Point point : pointList) {
					HeatMapVO hm = new HeatMapVO();
					hm.setValue(o.getInteger("count"));
					hm.setX(point.getX());
					hm.setY(point.getY());
					hm.setXy(String.valueOf(point.getX())+ String.valueOf(point.getY()));
					list.add(hm);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		// 对计算结果进行合并统计	
		List<HeatMapVO> countResult = list.stream().collect(Collectors.toMap(HeatMapVO::getXy, a -> a, (v1, v2)-> {
		    v1.setValue(v1.getValue() + v2.getValue());
			return v1;
			})).values().stream().collect(Collectors.toList());
			return countResult ;
	} 
	
	public List<NavigateVO> initNavigate(String floorId, String uid, String buildingId) {
		TrailDetailQueryDTO queryDTO = new TrailDetailQueryDTO();
		queryDTO.setDate(LocalDate.now());
		queryDTO.setStartTime(LocalTime.of(9, 0, 0)); //DateUtils.getStartTime()
		queryDTO.setEndTime(LocalTime.now());
		queryDTO.setMarkNodes(getNaviPathCamera(floorId));
		queryDTO.setNodeCode(floorId);
		//String result = feignClient.getTrailDetail(queryDTO);
		String result = restTemplateUtils.httpRequestByJSON(JSON.toJSONString(queryDTO), "http://58.251.16.82:7000/trail/detail");
		logger.info("init<>trail/detail result=======>{}", result);
		if(StringUtils.isBlank(result)) {return new ArrayList<>();}
		CameraDataDTO cd = computeCameraData(result, buildingId);
		if(cd == null) {return new ArrayList<>();}
		// 获取初始化数据
		List<NavigateVO> initList = getNavigateData(cd);
		Map<String, Object> m = new HashMap<>();
		m.put("lastDataId", cd.getLastDataId());
		m.put("lastTime", DateUtils.formatDateTime(cd.getLastTime()));
		redisUtil.set(buildingId + cd.getNodeCode() + uid, m);
		return initList;
		
	}
	
	public ResultDataVO historyNavigate(HistoryQueryParam param){
		ResultDataVO rd = new ResultDataVO();
		TrailDetailIncQueryDTO queryDTO = new TrailDetailIncQueryDTO();
		List<Integer> sexList = new ArrayList<>();
		List<Integer> ageList = new ArrayList<>();
		if(StringUtils.isNotBlank(param.getAge())) {
			for(String s : param.getAge().split(",")){
				ageList.add(Integer.valueOf(s));
			}
		}
		if(StringUtils.isNotBlank(param.getSex())) {
			for(String s : param.getSex().split(",")){
				sexList.add(Integer.valueOf(s));
			}
		}
		
		queryDTO.setLastTime(DateUtils.localDateTimePares(param.getDate()));
		queryDTO.setMarkNodes(getNaviPathCamera(param.getFloorId()));
		queryDTO.setNodeCode(param.getFloorId());
		queryDTO.setAgeList(ageList);
		queryDTO.setSexList(sexList);
		queryDTO.setLastDataId(Long.valueOf(param.getLastDataId()));
		//String result = feignClient.getTrailDetailInc(queryDTO);
		String result = restTemplateUtils.httpRequestByTrailDetailInc(queryDTO, "http://58.251.16.82:7000/trail/detail/inc", param.getDate());
		logger.info("history<>detail/inc result=======>{}", result);
		if(StringUtils.isBlank(result)) {return rd;}
		CameraDataDTO cd = computeCameraData(result, param.getBuildingId());
		if(cd == null) {return rd;}
		List<NavigateVO> historyList = getNavigateData(cd);
		rd.setLastDataId(cd.getLastDataId());
		rd.setLastTime(DateUtils.formatDateTime(cd.getLastTime()));
		rd.setLines(historyList);
		return rd;
	}
	
	public List<NavigateVO> getNavigateData(CameraDataDTO cd) {
		PersonInfo p = new PersonInfo();
		List<NavigateVO> result = new ArrayList<>();
		try {
			p.setLastDataId(cd.getLastDataId()); //cd.getLastDataId() //510016275620171776L
			p.setLastTime(cd.getLastTime()); //cd.getLastTime() //"2021-11-08 17:02:30"
			p.setFloorId(cd.getNodeCode()); //cd.getNodeCode() //"0001-0002-0002-000K"
			p.setCreateDate(cd.getCreateData());
			List<NavigateVO> pi = navigateDao.getPersonInfoByLastDataId(p);
			if(pi == null) {return result;}
			List<PersonlinesVO> groupLines = navigateDao.getGroupLines(p);
			for(PersonlinesVO pv : groupLines) {
				NavigateVO nv = new NavigateVO();
				p.setStartId(pv.getStartId());
				p.setEndId(pv.getEndId());
				p.setUserId(pv.getUserId());
				List<PersonlinesVO> LinesByGroup =  navigateDao.getLinesByGroup(p);
				NavigateVO nvOptional = pi.stream().filter(
						item -> item.getUserId().longValue() == pv.getUserId().longValue()).findFirst().get();
				nv.setUserId(nvOptional.getUserId());
				nv.setAge(nvOptional.getAge());
				nv.setSex(nvOptional.getSex());
				nv.setUserId(pv.getUserId());
				nv.setPoints(LinesByGroup);
				result.add(nv);
			}
		} catch (Exception e) {
			logger.error("执行getNavigateData方法异常：{}", e.getMessage());
		}
		navigateDao.delLines(p);
		navigateDao.delPerson(p);
		return result;
	}
	
	
	public JSONObject getBuilding(String buildingId) {
		if(StringUtils.isBlank(buildingId)) {
			buildingId = apiConfig.getBuildingId();
		}
		MultiValueMap<String, String> p = new LinkedMultiValueMap<>();
		String result = restTemplateUtils.httpRequestByForm(p, "/api/third/building");
		JSONObject obj = JSON.parseObject(result);
		JSONArray array = obj.getJSONArray("data");
		for(int i = 0; i < array.size(); i++ ) {
			JSONObject build = array.getJSONObject(i);
			String buildCode = build.getString("code");
			if(buildCode.equals(buildingId)) {
				return build;
			}
		}
		return null;
	}

	public JSONArray getFloor(String buildingId) {
		if(StringUtils.isBlank(buildingId)) {
			buildingId = apiConfig.getBuildingId();
		}
		MultiValueMap<String, String> p = new LinkedMultiValueMap<>();
		// 业务参数
		p.add("code", buildingId);
		String result = restTemplateUtils.httpRequestByForm(p, "/api/third/floor");
		JSONObject obj = JSON.parseObject(result);
		JSONArray array = obj.getJSONArray("data");
		return array;
	}
	
	public JSONObject cameraObj(String floorCode){
		String pathResult = getPath(apiConfig.getBuildingId(), floorCode);
		JSONObject json = JSONObject.parseObject(pathResult);
		JSONObject data = json.getJSONObject("data");
		JSONArray pointsArray = data.getJSONArray("points");
		JSONObject cameraObj = new JSONObject();
		for (Iterator<?> iterator = pointsArray.iterator(); iterator.hasNext();) {
			JSONObject obj = (JSONObject) iterator.next();
			String camera = obj.getString("camera");
			if(StringUtils.isNotBlank(camera)) {
				cameraObj.put(camera, obj);
			}
		}
		return cameraObj;
	}
	

	public List<String> getNaviPathCamera(String floorCode){
		String pathResult = getPath(apiConfig.getBuildingId(), floorCode);
		JSONObject json = JSONObject.parseObject(pathResult);
		JSONObject data = json.getJSONObject("data");
		JSONArray pointsArray = data.getJSONArray("points");
		Set<String> cameraSet = new HashSet<>();
		for (Iterator<?> iterator = pointsArray.iterator(); iterator.hasNext();) {
			JSONObject obj = (JSONObject) iterator.next();
			String camera = obj.getString("camera");
			if(StringUtils.isNotBlank(camera)) {
				cameraSet.add(camera);
			}
		}
		return new ArrayList<>(cameraSet);
	}
	
	
	
	/**
	 * 获取poi
	 * @param floorCode
	 * @return
	 */
	public List<String> getPoi(String floorCode) {
		MultiValueMap<String, String> p = new LinkedMultiValueMap<>();
		// 业务参数
		p.add("buildingCode", apiConfig.getBuildingId());
		p.add("floorCode", floorCode);
		Object cacheObj = redisUtil.get("poi-" + floorCode);
		String result;
		if(cacheObj == null) {
			result = restTemplateUtils.httpRequestByForm(p, "/api/third/floor/poi");
			redisUtil.set("poi-" + floorCode, result, apiConfig.getCacheExpire());
		}else {
			result = (String) cacheObj;
		}
		
		JSONObject poiObj = JSON.parseObject(result);
		JSONArray array = poiObj.getJSONArray("data");
		List<String> markNodes = new ArrayList<>();
		JSONObject obj;
		for(int i = 0; i < array.size(); i++) {
			obj = new JSONObject();
			obj = array.getJSONObject(i);
			markNodes.add(obj.getString("id"));
		}
		return markNodes;
	}

	public String getPath(String buildingCode, String floorCode) {
		MultiValueMap<String, String> p = new LinkedMultiValueMap<>();
		// 业务参数
		p.add("buildingCode", buildingCode);
		p.add("floorCode", floorCode);
		Object obj = redisUtil.get("path-" + buildingCode + floorCode);
		String result;
		if(obj == null) {
			result = restTemplateUtils.httpRequestByForm(p, "/api/third/floor/path");
			redisUtil.set("path-" + buildingCode + floorCode, result, apiConfig.getCacheExpire());
		}else {
			result = (String) obj;
		}
		return result;
	}
	
	
	/**
	 * 初始化算法工具NaviTool
	 * @param floorCode
	 * @return
	 */
	public NaviTool initNaviTool(String floorCode, String buildingId) {
		if(StringUtils.isBlank(buildingId)) {
			buildingId = apiConfig.getBuildingId();
		}
		ArrayList<Point> points = new ArrayList<>();
		ArrayList<Path> paths = new ArrayList<>();
		// 获取路径数据
		String pathResult = getPath(buildingId, floorCode);
		JSONObject json = JSONObject.parseObject(pathResult);
		JSONObject data = json.getJSONObject("data");
		JSONArray pointsArray = data.getJSONArray("points");
		JSONArray pathsArray = data.getJSONArray("paths");
		for (Iterator<?> iterator = pointsArray.iterator(); iterator.hasNext();) {
			JSONObject obj = (JSONObject) iterator.next();
			points.add(new Point(obj.getString("id"), obj.getString("camera"), 
					obj.getDoubleValue("x"), obj.getDoubleValue("y")));
			
		}
		for (Iterator<?> iterator = pathsArray.iterator(); iterator.hasNext();) {
			JSONObject obj = (JSONObject) iterator.next();
			paths.add(new Path(obj.getString("id"), obj.getString("start"), obj.getString("end"),
					obj.getDoubleValue("length"), obj.getDoubleValue("width")));
		}
		return new NaviTool(points, paths);
	}

	public List<Point> getPoints(String startId, String endId){
		List<Point> pointList = new ArrayList<>();
		NaviTool naviTool = this.initNaviTool("0001-0002-0002-000K", apiConfig.getBuildingId());
		try {
			pointList = naviTool.navigateByCamera(startId, endId);
		} catch (Exception e) {
			return  pointList;
		}
		return  pointList;
	}

	/**
	 * 计算路径
	 * @param cameraData
	 * @return
	 */
	public CameraDataDTO computeCameraData(String cameraData, String buildingId) {
		CameraDataDTO cd = new CameraDataDTO();
		JSONObject obj = JSONObject.parseObject(cameraData);
		if(obj.getInteger("code") != 0) {
			return null;
		}
		JSONObject data = obj.getJSONObject("data");
		cd = JSON.toJavaObject(data, CameraDataDTO.class);
		// 初始化NaviTool
		NaviTool naviTool = this.initNaviTool(cd.getNodeCode(), buildingId); 
		Date createData = new Date();
		cd.setCreateData(createData);
		// 循环lines节点
		for (LinesDTO l : cd.getLines()) {
			List<Point> pointList = new ArrayList<>();
			logger.info("====================START==================");
			logger.info("开始路径计算：cameraStart->{}-----cameraEnd--->{}", l.getSourceCode(), l.getTargetCode());
			// 监控下的人员信息
			List<PersonInfo> pList = new ArrayList<>();
			// 循环lines下的list(人员信息)
			PersonInfo p;
			for (UserInfoDTO u : l.getList()) {
				p = new PersonInfo();
				p.setUserId(u.getReid());
				p.setAge(u.getAge());
				p.setFloorId(cd.getNodeCode());
				p.setSex(u.getSex());
				p.setLastTime(cd.getLastTime());
				p.setLastDataId(cd.getLastDataId());
				pList.add(p);
				try {
					// 通过NaviTool提供的算法得出Points
					pointList = naviTool.navigateByCamera(l.getSourceCode(), l.getTargetCode());
				} catch (Exception e) {
					logger.info("路径计算失败=========》{}", e.getMessage());
					continue;
				}
				logger.info("路径计算成功，计算结果===》{}", JSON.toJSON(pointList));
				// 一个用户走过的线路集合
				List<PersonLines> linecList = new ArrayList<>();
				// 保存根据算法算出的路径结果
				PersonLines pl;
				for (Point e : pointList) {
					pl = new PersonLines();
					pl.setUserId(u.getReid());
					pl.setX(e.getX());
					pl.setY(e.getY());
					pl.setLastTime(cd.getLastTime());
					pl.setLastDataId(cd.getLastDataId());
					pl.setFloorId(cd.getNodeCode());
					pl.setOffsetX(e.getOffsetX());
					pl.setOffsetY(e.getOffsetY());
					pl.setStartId(l.getSourceCode());
					pl.setEndId(l.getTargetCode());
					pl.setCreateData(createData);
					linecList.add(pl);
				}
				// 保存人员的路线信息
				navigateDao.batchInsertUserLines(linecList);
				
			}
			// 保存人员信息
			if(pointList != null && pointList.size() > 0) {
				navigateDao.batchInsertPerson(pList);
			}
			logger.info("=======================END=========================");
		}
		return cd;
	}
	
	
	public String getShopInfo(String shopCode){
		String result = restTemplateUtils.httpRequestByGet("http://58.251.16.82:7000/merchant/shop/get/byShopCode?shopCode=" + shopCode);
		//String result = mFeignClient.getShopInfo(shopCode);
		return result;
	}
	
	public String getShopFlow(ShopFlowQueryParam p) {
		String result = restTemplateUtils.httpRequestByJSON(JSON.toJSONString(p), "http://58.251.16.82:7000/shop/flow/byShopCode");
		//String result = feignClient.getShopFlow(p);
		return result;
	}
	
}
